1
|
|
|
var assert = require('assert'); |
2
|
|
|
var sjcl = require('sjcl'); |
3
|
|
|
var KeyDerivation = require('./keyderivation'); |
4
|
|
|
var randomBytes = require('randombytes'); |
5
|
|
|
|
6
|
|
|
var Encryption = { |
7
|
|
|
defaultSaltLen: 10, /* can permit changes, no more than 512 bit (128 bytes) */ |
8
|
|
|
tagLenBits: 128, /* can permit changes */ |
9
|
|
|
ivLenBits: 128, /* fixed */ |
10
|
|
|
ivLenWords: 128 / 32 |
11
|
|
|
}; |
12
|
|
|
|
13
|
|
|
Encryption.generateSalt = function() { |
14
|
|
|
return randomBytes(this.defaultSaltLen); |
15
|
|
|
}; |
16
|
|
|
|
17
|
|
|
Encryption.generateIV = function() { |
18
|
|
|
return randomBytes(this.ivLenBits / 8); |
19
|
|
|
}; |
20
|
|
|
|
21
|
|
|
Encryption.encrypt = function(pt, pw, iterations) { |
22
|
|
|
var salt = this.generateSalt(); |
23
|
|
|
var iv = this.generateIV(); |
24
|
|
|
|
25
|
|
|
iterations = typeof iterations === 'undefined' ? KeyDerivation.defaultIterations : iterations; |
26
|
|
|
return this.encryptWithSaltAndIV(pt, pw, salt, iv, iterations); |
27
|
|
|
}; |
28
|
|
|
|
29
|
|
|
Encryption.encryptWithSaltAndIV = function(pt, pw, saltBuf, iv, iterations) { |
30
|
|
|
assert(pt instanceof Buffer, 'pt must be provided as a buffer'); |
|
|
|
|
31
|
|
|
assert(pw instanceof Buffer, 'pw must be provided as a buffer'); |
32
|
|
|
assert(iv instanceof Buffer, 'IV must be provided as a buffer'); |
33
|
|
|
assert(saltBuf instanceof Buffer, 'saltBuff must be provided as a buffer'); |
34
|
|
|
assert(iv.length === 16, 'IV must be exactly 16 bytes'); |
35
|
|
|
|
36
|
|
|
var SL = (new Buffer(1)); |
37
|
|
|
var S = saltBuf; |
38
|
|
|
var I = new Buffer(4); |
39
|
|
|
SL.writeUInt8(saltBuf.length); |
40
|
|
|
I.writeUInt32LE(iterations); |
41
|
|
|
var header = SL.toString('hex') + S.toString('hex') + I.toString('hex'); |
42
|
|
|
|
43
|
|
|
var key = sjcl.codec.hex.toBits(KeyDerivation.compute(pw, saltBuf, iterations).toString('hex')); |
44
|
|
|
var ct_t = sjcl.mode.gcm.encrypt( |
45
|
|
|
new sjcl.cipher.aes(key), |
46
|
|
|
sjcl.codec.hex.toBits(pt.toString('hex')), |
47
|
|
|
sjcl.codec.hex.toBits(iv.toString('hex')), |
48
|
|
|
sjcl.codec.hex.toBits(header), |
49
|
|
|
this.tagLenBits |
50
|
|
|
); |
51
|
|
|
|
52
|
|
|
// iter || saltLen8 || salt || iv || tag || ct |
53
|
|
|
return new Buffer([header, iv.toString('hex'), sjcl.codec.hex.fromBits(ct_t)].join(''), 'hex'); |
54
|
|
|
}; |
55
|
|
|
|
56
|
|
|
Encryption.decrypt = function(ct, pw) { |
57
|
|
|
assert(ct instanceof Buffer, 'cipherText must be provided as a Buffer'); |
|
|
|
|
58
|
|
|
assert(pw instanceof Buffer, 'password must be provided as a Buffer'); |
59
|
|
|
var copy = new Buffer(ct, 'hex'); |
60
|
|
|
var c = 0; |
61
|
|
|
|
62
|
|
|
var saltLen = copy.readUInt8(c) ; c += 1; |
63
|
|
|
var salt = copy.slice(1, c + saltLen); c += saltLen; |
64
|
|
|
var iterations = copy.readUInt32LE(c); c += 4; |
65
|
|
|
var header = copy.slice(0, c); |
66
|
|
|
|
67
|
|
|
var iv = copy.slice(c, 16 + c); c += 16; |
68
|
|
|
var ct_t = copy.slice(c); |
69
|
|
|
|
70
|
|
|
// SaltBuf is required for KeyDerivation. Convert to sjcl where required. |
71
|
|
|
var key = KeyDerivation.compute(pw, salt, iterations); |
72
|
|
|
var plainText = sjcl.mode.gcm.decrypt( |
73
|
|
|
new sjcl.cipher.aes(sjcl.codec.hex.toBits(key.toString('hex'))), |
74
|
|
|
sjcl.codec.hex.toBits(ct_t.toString('hex')), |
75
|
|
|
sjcl.codec.hex.toBits(iv.toString('hex')), |
76
|
|
|
sjcl.codec.hex.toBits(header.toString('hex')), |
77
|
|
|
this.tagLenBits |
78
|
|
|
); |
79
|
|
|
return new Buffer(sjcl.codec.hex.fromBits(plainText), 'hex'); |
80
|
|
|
}; |
81
|
|
|
|
82
|
|
|
module.exports = Encryption; |
83
|
|
|
|
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.